package com.sy.notice.biz.eventmanage.core;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sy.notice.biz.eventdefine.entity.EventTask;
import com.sy.notice.biz.eventnotice.properties.DingTalkProperties;
import com.sy.notice.biz.eventnotice.properties.TxSmsProperties;
import com.sy.notice.biz.eventnotice.service.EventNoticeManager;
import com.sy.scheduled.common.annotation.SuperScheduledInteriorOrder;
import com.sy.scheduled.common.annotation.SuperScheduledOrder;
import com.sy.scheduled.common.utils.AnnotationUtils;
import com.sy.scheduled.common.utils.proxy.Chain;
import com.sy.scheduled.common.utils.proxy.Point;
import com.sy.scheduled.common.utils.proxy.ProxyUtils;
import com.sy.scheduled.core.RunnableInterceptor.RunnableBaseInterceptor;
import com.sy.scheduled.core.RunnableInterceptor.SuperScheduledRunnable;
import com.sy.scheduled.core.RunnableInterceptor.strengthen.BaseStrengthen;
import com.sy.scheduled.core.ScheduledFutureFactory;
import com.sy.scheduled.core.SuperScheduledConfig;
import com.sy.scheduled.core.SuperScheduledManager;
import com.sy.scheduled.exception.SuperScheduledException;
import com.sy.scheduled.model.ScheduledSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

/**
 * @author 舒祥
 * @date 2022-01-16 12:09
 * @desc
 */
@Service
@Slf4j(topic = "syslog")
public class ScheduledManager implements ApplicationContextAware {

    @Autowired
    private SuperScheduledConfig superScheduledConfig;

    @Autowired
    private SuperScheduledManager superScheduledManager;

    /**
     * 定时任务执行线程
     */
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private ApplicationContext applicationContext;


    private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Value("${event.file.path:/home/sy/event.json}")
    private String path;

    @Autowired
    private DingTalkProperties dingTalkProperties;

    @Autowired
    private TxSmsProperties txSmsProperties;

    private EventNoticeManager eventNoticeManager;

    @PostConstruct
    public void init() throws IOException {
        eventNoticeManager = new EventNoticeManager(dingTalkProperties, txSmsProperties);
        String result = FileUtils.readFileToString(new File(path), StandardCharsets.UTF_8);
        Map map = JSON.parseObject(result, Map.class);
        List<JSONObject> events = (List) map.get("events");
        for (JSONObject event : events) {
            EventTask eventTask = JSON.parseObject(JSON.toJSONString(event), EventTask.class);
            log.info("init eventTask:{}", eventTask);
            try {
                addScheduledSource(eventTask);
            } catch (Exception e) {
                log.error("addScheduledSource error", e);
            }
        }
    }

    /**
     * 取消定时任务
     *
     * @param name 任务的key
     */
    public void cancelScheduledFuture(String name) {
        ScheduledFuture scheduledFuture = superScheduledConfig.getScheduledFuture(name);
        if (scheduledFuture == null) {
            log.info(df.format(LocalDateTime.now()) + "任务" + name + "未发现.");
            return;
        }
        scheduledFuture.cancel(true);
        Map<String, ScheduledFuture> nameToScheduledFuture = superScheduledConfig.getNameToScheduledFuture();
        nameToScheduledFuture.remove(name);
        superScheduledConfig.removeNoticeScheduledFuture(name);
        log.info(df.format(LocalDateTime.now()) + "任务" + name + "已经终止...");
    }

    /**
     * @author:舒祥
     * @createTime: 2022/4/15 11:05
     * @description:
     * @modifyTime:
     * @modifyDescription:
     */
    public void cancelAllScheduledFuture() {
        Map<String, ScheduledFuture> map = superScheduledConfig.getNameToScheduledFuture();
        map.forEach((k, v) -> {
            boolean cancel = v.cancel(true);
            log.info("任务:{}已经终止.结果:{}", k, cancel);
        });
        map.clear();
        superScheduledConfig.getNoticeToScheduledSource().clear();
    }

    /**
     * @author:舒祥
     * @createTime: 2022/1/16 15:21
     * @description: 定时任务的代理添加
     * @modifyTime:
     * @modifyDescription:
     */
    public void addScheduledFuture(EventTask eventTask) throws Exception {
        String name = eventTask.getKey();
        ScheduledSource scheduledSource = addScheduledSource(eventTask);
        //获取所有增强类
        String[] baseStrengthenBeanNames = applicationContext.getBeanNamesForType(BaseStrengthen.class);
        //创建执行控制器
        SuperScheduledRunnable runnable = new SuperScheduledRunnable();
        runnable.setMethod(scheduledSource.getMethod());
        runnable.setBean(scheduledSource.getBean());
        runnable.setArgs(scheduledSource.getArgs());
        //将增强器代理成point
        List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);
        for (String baseStrengthenBeanName : baseStrengthenBeanNames) {
            Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName);
            //获取秩序顺序
            SuperScheduledOrder orderAnnotation = baseStrengthenBean.getClass().getAnnotation(SuperScheduledOrder.class);
            SuperScheduledInteriorOrder interiorOrderAnnotation = baseStrengthenBean.getClass().getAnnotation(SuperScheduledInteriorOrder.class);
            //创建代理
            Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable));
            proxy.setOrder(orderAnnotation == null ? 0 : orderAnnotation.value());
            proxy.setInteriorOrder(interiorOrderAnnotation == null ? null : interiorOrderAnnotation.value());
            proxy.setSuperScheduledName(name);
            proxy.setScheduledSource(scheduledSource);
            //所有的points连接起来
            points.add(proxy);
        }
        //按照执行顺序排序
        AnnotationUtils.superScheduledOrderSort(points);
        runnable.setChain(new Chain(points));
        //添加缓存中
        superScheduledConfig.addRunnable(name, runnable::invoke);
        //执行方法
        try {
            ScheduledFuture<?> schedule = ScheduledFutureFactory.create(threadPoolTaskScheduler
                    , scheduledSource, runnable::invoke);
            superScheduledConfig.addScheduledFuture(name, schedule);
            log.info(df.format(LocalDateTime.now()) + "任务" + name + "已经启动...");
        } catch (Exception e) {
            throw new SuperScheduledException("任务" + name + "启动失败，错误信息：" + e.getLocalizedMessage());
        }
    }


    public ScheduledSource addScheduledSource(EventTask eventTask) throws Exception {
        Class<? extends EventNoticeManager> aClass = eventNoticeManager.getClass();
        Method sendMsg = aClass.getMethod("sendMsg", EventTask.class);
        ScheduledSource scheduledSource = new ScheduledSource(eventTask.getEventRules(), sendMsg, eventNoticeManager, eventTask);
        superScheduledConfig.addNoticeScheduledSource(eventTask.getKey(), scheduledSource);
        return scheduledSource;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
